package be.novelfaces.component.util.attribute;

import static be.novelfaces.component.util.attribute.AttributeType.AUTOCOMPLETE;
import static be.novelfaces.component.util.attribute.AttributeType.CLASS;
import static be.novelfaces.component.util.attribute.AttributeType.ID;
import static be.novelfaces.component.util.attribute.AttributeType.NAME;
import static be.novelfaces.component.util.attribute.AttributeType.VALUE;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import be.novelfaces.component.util.RenderUtils;
import be.novelfaces.component.util.RenderUtilsFactory;
import be.novelfaces.component.util.element.ElementWriter;
import be.novelfaces.component.util.element.InputType;

public class AttributesWriterBuilder {

	static final String TYPE = "type";
	static final String OFF = "off";
	private FacesContext context;
	private UIComponent component;
	private ResponseWriter writer;

	private PassThroughAttributes passThroughAttributes = new PassThroughAttributes();
	private Set<Attribute> attributes = new LinkedHashSet<Attribute>();
	private RenderUtils renderUtils = RenderUtilsFactory.getInstance();
	private final ElementWriter<?> elementWriter;

	public AttributesWriterBuilder(FacesContext context, UIComponent component) {
		this(context, component, null);
	}

	public AttributesWriterBuilder(FacesContext context, UIComponent component,
			ElementWriter<?> elementWriter) {
		super();
		this.context = context;
		this.component = component;
		this.elementWriter = elementWriter;
		writer = context.getResponseWriter();
	}

	public ElementWriter<?> writeAttributes() throws IOException {
		for (Attribute attribute : attributes) {
			writer.writeAttribute(attribute.getName(), attribute.getValue(),
					attribute.getProperty());
		}
		renderUtils.renderPassThroughAttributes(context, component,
				passThroughAttributes);
		return elementWriter;
	}

	protected PassThroughAttributes getPassThroughAttributes() {
		return passThroughAttributes;
	}

	public AttributesWriterBuilder withId() {
		addAttribute(ID.getName(), ID.getProperty(),
				component.getClientId(context));
		return this;
	}

	public AttributesWriterBuilder withAutocomplete() {
		if (OFF.equals(component.getAttributes()
				.get(AUTOCOMPLETE.getProperty()))) {
			attributes.add(new Attribute(AUTOCOMPLETE.getName(), OFF,
					AUTOCOMPLETE.getProperty()));
		}
		return this;
	}

	public AttributesWriterBuilder withStyleClass() {
		addAttribute(CLASS.getName(),
				CLASS.getProperty(),
				component.getAttributes().get(CLASS.getProperty()));
		return this;
	}

	public AttributesWriterBuilder withValue() {
		addAttribute(VALUE.getName(),
				renderUtils.getStringValue(context, component));
		return this;
	}

	public AttributesWriterBuilder withName() {
		addAttribute(NAME.getName(), NAME.getProperty(),
				component.getClientId(context));
		return this;
	}

	public AttributesWriterBuilder withGlobalAttributes() {
		withStyleClass();
		passThroughAttributes.withGlobalAttributes();
		return this;
	}

	public AttributesWriterBuilder withFormEvents() {
		passThroughAttributes.withFormEvents();
		return this;
	}

	public AttributesWriterBuilder withKeyboardEvents() {
		passThroughAttributes.withKeyboardEvents();
		return this;
	}

	public AttributesWriterBuilder withMouseEvents() {
		passThroughAttributes.withMouseEvents();
		return this;
	}
	
	public AttributesWriterBuilder withInputType(InputType inputType) {
		addAttribute(TYPE, null, inputType.getName());
		return this;
	}

	public AttributesWriterBuilder withAttribute(String name) {
		addAttribute(name, component.getAttributes().get(name));
		return this;
	}
	
	public AttributesWriterBuilder withAttribute(String name, String property, Object value) {
		addAttribute(name, property, value);
		return this;
	}
	
	public AttributesWriterBuilder withAttribute(AttributeType attributeType, Object value) {
		addAttribute(attributeType.getName(), attributeType.getProperty(), value);
		return this;
	}
	
	public AttributesWriterBuilder withAttribute(AttributeType attributeType) {
		addAttribute(attributeType.getName(), component.getAttributes().get(attributeType.getProperty()));
		return this;
	}

	public AttributesWriterBuilder withPassThroughAttribute(PassThroughAttribute attribute) {
		passThroughAttributes.add(attribute);
		return this;
	}

	private void addAttribute(String name, Object value) {
		addAttribute(name, name, value);
	}

	private void addAttribute(String name, String property, Object value) {
		if (!renderUtils.isDefaultAttributeValue(value)) {
			attributes.add(new Attribute(name, value, property));
		}
	}

}
